"#ifndef _GLTF_PBR_SHADING_FXH_\n"
"#define _GLTF_PBR_SHADING_FXH_\n"
"\n"
"#include \"GLTF_PBR_Structures.fxh\"\n"
"#include \"PBR_Common.fxh\"\n"
"#include \"ShaderUtilities.fxh\"\n"
"\n"
"#ifndef GLTF_PBR_MANUAL_SRGB\n"
"#   define  GLTF_PBR_MANUAL_SRGB    1\n"
"#endif\n"
"\n"
"#ifndef SRGB_FAST_APPROXIMATION\n"
"#   define  SRGB_FAST_APPROXIMATION 1\n"
"#endif\n"
"\n"
"#define GLTF_PBR_USE_ENV_MAP_LOD\n"
"#define GLTF_PBR_USE_HDR_CUBEMAPS\n"
"\n"
"float GetPerceivedBrightness(float3 rgb)\n"
"{\n"
"    return sqrt(0.299 * rgb.r * rgb.r + 0.587 * rgb.g * rgb.g + 0.114 * rgb.b * rgb.b);\n"
"}\n"
"\n"
"// https://github.com/KhronosGroup/glTF/blob/master/extensions/2.0/Khronos/KHR_materials_pbrSpecularGlossiness/examples/convert-between-workflows/js/three.pbrUtilities.js#L34\n"
"float GLTF_PBR_SolveMetallic(float3 diffuse,\n"
"                             float3 specular,\n"
"                             float  oneMinusSpecularStrength)\n"
"{\n"
"    const float c_MinReflectance = 0.04;\n"
"    float specularBrightness = GetPerceivedBrightness(specular);\n"
"    if (specularBrightness < c_MinReflectance)\n"
"    {\n"
"        return 0.0;\n"
"    }\n"
"\n"
"    float diffuseBrightness = GetPerceivedBrightness(diffuse);\n"
"\n"
"    float a = c_MinReflectance;\n"
"    float b = diffuseBrightness * oneMinusSpecularStrength / (1.0 - c_MinReflectance) + specularBrightness - 2.0 * c_MinReflectance;\n"
"    float c = c_MinReflectance - specularBrightness;\n"
"    float D = b * b - 4.0 * a * c;\n"
"\n"
"    return clamp((-b + sqrt(D)) / (2.0 * a), 0.0, 1.0);\n"
"}\n"
"\n"
"\n"
"float3 SRGBtoLINEAR(float3 srgbIn)\n"
"{\n"
"#ifdef GLTF_PBR_MANUAL_SRGB\n"
"#   ifdef SRGB_FAST_APPROXIMATION\n"
"    	float3 linOut = pow(srgbIn.xyz, float3(2.2, 2.2, 2.2));\n"
"#   else\n"
"	    float3 bLess  = step(float3(0.04045, 0.04045, 0.04045), srgbIn.xyz);\n"
"	    float3 linOut = mix( srgbIn.xyz/12.92, pow((srgbIn.xyz + float3(0.055, 0.055, 0.055)) / 1.055, float3(2.4, 2.4, 2.4)), bLess );\n"
"#   endif\n"
"	    return linOut;\n"
"#else\n"
"	return srgbIn;\n"
"#endif\n"
"}\n"
"\n"
"float4 SRGBtoLINEAR(float4 srgbIn)\n"
"{\n"
"    return float4(SRGBtoLINEAR(srgbIn.xyz), srgbIn.w);\n"
"}\n"
"\n"
"\n"
"float3 GLTF_PBR_ApplyDirectionalLight(float3 lightDir, float3 lightColor, SurfaceReflectanceInfo srfInfo, float3 normal, float3 view)\n"
"{\n"
"    float3 pointToLight = -lightDir;\n"
"    float3 diffuseContrib, specContrib;\n"
"    float  NdotL;\n"
"    BRDF(pointToLight, normal, view, srfInfo, diffuseContrib, specContrib, NdotL);\n"
"    // Obtain final intensity as reflectance (BRDF) scaled by the energy of the light (cosine law)\n"
"    float3 shade = (diffuseContrib + specContrib) * NdotL;\n"
"    return lightColor * shade;\n"
"}\n"
"\n"
"\n"
"// Find the normal for this fragment, pulling either from a predefined normal map\n"
"// or from the interpolated mesh normal and tangent attributes.\n"
"float3 GLTF_PBR_PerturbNormal(in float3 dPos_dx,\n"
"                              in float3 dPos_dy,\n"
"                              in float2 dUV_dx,\n"
"                              in float2 dUV_dy,\n"
"                              in float3 Normal,\n"
"                              in float3 TSNormal,\n"
"                              bool      HasUV,\n"
"                              bool      IsFrontFace)\n"
"{\n"
"    // Retrieve the tangent space matrix\n"
"    float NormalLen = length(Normal);\n"
"    float3 ng;\n"
"    if (NormalLen > 1e-5)\n"
"    {\n"
"        ng = Normal/NormalLen;\n"
"    }\n"
"    else\n"
"    {\n"
"        ng = normalize(cross(dPos_dx, dPos_dy));\n"
"#if (defined(GLSL) || defined(GL_ES)) && !defined(VULKAN)\n"
"        // In OpenGL screen is upside-down, so we have to invert the vector\n"
"        ng *= -1.0;\n"
"#endif\n"
"    }\n"
"\n"
"    if (HasUV)\n"
"    {\n"
"        return TransformTangentSpaceNormalGrad(dPos_dx, dPos_dy, dUV_dx, dUV_dy, ng, TSNormal * (IsFrontFace ? +1.0 : -1.0));\n"
"    }\n"
"    else\n"
"    {\n"
"        return ng * (IsFrontFace ? +1.0 : -1.0);\n"
"    }\n"
"}\n"
"\n"
"\n"
"struct GLTF_PBR_IBL_Contribution\n"
"{\n"
"    float3 f3Diffuse;\n"
"    float3 f3Specular;\n"
"};\n"
"\n"
"// Calculation of the lighting contribution from an optional Image Based Light source.\n"
"// Precomputed Environment Maps are required uniform inputs and are computed as outlined in [1].\n"
"// See our README.md on Environment Maps [3] for additional discussion.\n"
"GLTF_PBR_IBL_Contribution GLTF_PBR_GetIBLContribution(\n"
"                        in SurfaceReflectanceInfo SrfInfo,\n"
"                        in float3                 n,\n"
"                        in float3                 v,\n"
"                        in float                  PrefilteredCubeMipLevels,\n"
"                        in Texture2D              BRDF_LUT,\n"
"                        in SamplerState           BRDF_LUT_sampler,\n"
"                        in TextureCube            IrradianceMap,\n"
"                        in SamplerState           IrradianceMap_sampler,\n"
"                        in TextureCube            PrefilteredEnvMap,\n"
"                        in SamplerState           PrefilteredEnvMap_sampler)\n"
"{\n"
"    float NdotV = clamp(dot(n, v), 0.0, 1.0);\n"
"\n"
"    float lod = clamp(SrfInfo.PerceptualRoughness * PrefilteredCubeMipLevels, 0.0, PrefilteredCubeMipLevels);\n"
"    float3 reflection = normalize(reflect(-v, n));\n"
"\n"
"    float2 brdfSamplePoint = clamp(float2(NdotV, SrfInfo.PerceptualRoughness), float2(0.0, 0.0), float2(1.0, 1.0));\n"
"    // retrieve a scale and bias to F0. See [1], Figure 3\n"
"    float2 brdf = BRDF_LUT.Sample(BRDF_LUT_sampler, brdfSamplePoint).rg;\n"
"\n"
"    float4 diffuseSample = IrradianceMap.Sample(IrradianceMap_sampler, n);\n"
"\n"
"#ifdef GLTF_PBR_USE_ENV_MAP_LOD\n"
"    float4 specularSample = PrefilteredEnvMap.SampleLevel(PrefilteredEnvMap_sampler, reflection, lod);\n"
"#else\n"
"    float4 specularSample = PrefilteredEnvMap.Sample(PrefilteredEnvMap_sampler, reflection);\n"
"#endif\n"
"\n"
"#ifdef GLTF_PBR_USE_HDR_CUBEMAPS\n"
"    // Already linear.\n"
"    float3 diffuseLight  = diffuseSample.rgb;\n"
"    float3 specularLight = specularSample.rgb;\n"
"#else\n"
"    float3 diffuseLight  = SRGBtoLINEAR(diffuseSample).rgb;\n"
"    float3 specularLight = SRGBtoLINEAR(specularSample).rgb;\n"
"#endif\n"
"\n"
"    GLTF_PBR_IBL_Contribution IBLContrib;\n"
"    IBLContrib.f3Diffuse  = diffuseLight * SrfInfo.DiffuseColor;\n"
"    IBLContrib.f3Specular = specularLight * (SrfInfo.Reflectance0 * brdf.x + SrfInfo.Reflectance90 * brdf.y);\n"
"    return IBLContrib;\n"
"}\n"
"\n"
"/// Calculates surface reflectance info\n"
"\n"
"/// \\param [in]  Workflow     - PBR workflow (PBR_WORKFLOW_SPECULAR_GLOSINESS or PBR_WORKFLOW_METALLIC_ROUGHNESS).\n"
"/// \\param [in]  BaseColor    - Material base color.\n"
"/// \\param [in]  PhysicalDesc - Physical material description. For Metallic-roughness workflow,\n"
"///                             \'g\' channel stores roughness, \'b\' channel stores metallic.\n"
"/// \\param [out] Metallic     - Metallic value used for shading.\n"
"SurfaceReflectanceInfo GLTF_PBR_GetSurfaceReflectance(int        Workflow,\n"
"                                                      float4     BaseColor,\n"
"                                                      float4     PhysicalDesc,\n"
"                                                      out float  Metallic)\n"
"{\n"
"    SurfaceReflectanceInfo SrfInfo;\n"
"\n"
"    float3 specularColor;\n"
"\n"
"    float3 f0 = float3(0.04, 0.04, 0.04);\n"
"\n"
"    // Metallic and Roughness material properties are packed together\n"
"    // In glTF, these factors can be specified by fixed scalar values\n"
"    // or from a metallic-roughness map\n"
"    if (Workflow == PBR_WORKFLOW_SPECULAR_GLOSINESS)\n"
"    {\n"
"        SrfInfo.PerceptualRoughness = 1.0 - PhysicalDesc.a; // glossiness to roughness\n"
"        f0 = PhysicalDesc.rgb;\n"
"\n"
"        // f0 = specular\n"
"        specularColor = f0;\n"
"        float oneMinusSpecularStrength = 1.0 - max(max(f0.r, f0.g), f0.b);\n"
"        SrfInfo.DiffuseColor = BaseColor.rgb * oneMinusSpecularStrength;\n"
"\n"
"        // do conversion between metallic M-R and S-G metallic\n"
"        Metallic = GLTF_PBR_SolveMetallic(BaseColor.rgb, specularColor, oneMinusSpecularStrength);\n"
"    }\n"
"    else if (Workflow == PBR_WORKFLOW_METALLIC_ROUGHNESS)\n"
"    {\n"
"        // Roughness is stored in the \'g\' channel, metallic is stored in the \'b\' channel.\n"
"        // This layout intentionally reserves the \'r\' channel for (optional) occlusion map data\n"
"        SrfInfo.PerceptualRoughness = PhysicalDesc.g;\n"
"        Metallic                    = PhysicalDesc.b;\n"
"\n"
"        SrfInfo.DiffuseColor  = BaseColor.rgb * (float3(1.0, 1.0, 1.0) - f0) * (1.0 - Metallic);\n"
"        specularColor         = lerp(f0, BaseColor.rgb, Metallic);\n"
"    }\n"
"\n"
"//#ifdef ALPHAMODE_OPAQUE\n"
"//    baseColor.a = 1.0;\n"
"//#endif\n"
"//\n"
"//#ifdef MATERIAL_UNLIT\n"
"//    gl_FragColor = float4(gammaCorrection(baseColor.rgb), baseColor.a);\n"
"//    return;\n"
"//#endif\n"
"\n"
"    SrfInfo.PerceptualRoughness = clamp(SrfInfo.PerceptualRoughness, 0.0, 1.0);\n"
"\n"
"    // Compute reflectance.\n"
"    float reflectance = max(max(specularColor.r, specularColor.g), specularColor.b);\n"
"\n"
"    SrfInfo.Reflectance0  = specularColor.rgb;\n"
"    // Anything less than 2% is physically impossible and is instead considered to be shadowing. Compare to \"Real-Time-Rendering\" 4th editon on page 325.\n"
"    SrfInfo.Reflectance90 = clamp(reflectance * 50.0, 0.0, 1.0) * float3(1.0, 1.0, 1.0);\n"
"\n"
"    return SrfInfo;\n"
"}\n"
"\n"
"#endif // _GLTF_PBR_SHADING_FXH_\n"
